/**
 * @file calculator.c
 * @author TK (timechicken45@gmail.com)
 * @brief calc function
 * @version 1.0
 * @date 2021-12-09
 * 
 * @copyright Copyright (c) 2021
 * 
 */
#include "calculator.h"

int16_t g_calculator_error_code = 0;

/**
 * @brief return result of the calculating.
 * 
 * @param s input string.
 * @return char* result string.
 */
char *calculate(char *s)
{

    int check_error_char_flag, check_error_brackets_flag;
    int32_t s_len;
    char *s_ptr;
    DEBUG("entry function.");

    /* apply for memory of buffer */
    s_len = strlen(s);
    s_ptr = (char *)calloc(s_len + 2, 1);
    strncpy(s_ptr, s, s_len);
    format_the_entered_string(s_ptr);
    INFO("%s||len:%d", s_ptr, (int)strlen(s_ptr));

    /* check error input error char */
    check_error_char_flag = calc_check_error_char(s_ptr);
    if (check_error_char_flag)
    {
        g_calculator_error_code = -1;
        DEBUG("exit function.");
        return s;
    }

    check_error_brackets_flag = calc_check_error_brackets(s_ptr);
    /* check error input brackets */
    if (check_error_brackets_flag)
    {
        g_calculator_error_code = -1;
        DEBUG("exit function.");
        return s;
    }

    Braces(s_ptr);

    DEBUG("exit function.");
    return s_ptr;
}

/**
 * @brief 取大括号进行优先计算
 * 
 * @param string 字符串
 * @return char* 无
 */
char *Braces(char *const string)
{
    //left_brace指针指向左边的大括号,brace指针用于存放大括号内的数,brace1用于缓存"{"到"}"的字符并用于删除
    char *str = string, *left_brace = NULL, *right_brace = NULL,
         *brace = NULL, *brace1 = NULL;
    DEBUG("entry function.");

    while ((strchr(str, '}') != NULL) && (strchr(str, '{') != NULL)) //检测大括号，没有就不成立
    {
        right_brace = strchr(str, '}');
        for (unsigned int i = 0; i < strlen(string); i++, str++)
        {
            if (*str == '\0')
                break;
            else if (*str == '}')
                break;
            else if (*str == '{')
                left_brace = str;
        }

        //开始申请空间存放"{"到"}"的字符
        brace = calloc(1024, 1);
        memset(brace, 0, 1023);                               //初始化brace内存
        left_brace++;                                         //指针右移开始复制{}中的字符串
        strncpy(brace, left_brace, right_brace - left_brace); //开始复制{}中到brace
        left_brace--;                                         //将{指针还原为原来的指向的{

        //准备替换"{"到"}"的字符
        brace1 = calloc(1024, 1);
        memset(brace1, 0, 1023);                               //初始化brace内存
        right_brace++;                                         //指针右移开始复制{}中的字符串
        strncpy(brace1, left_brace, right_brace - left_brace); //给被删除的字符串加上{}
        right_brace--;                                         //将} 指针还原为原来的指向的}
        //开始替换字符
        str = string; //复原str指针

        //还原指针
        strrpc(str, brace1, Parenthesis(brace)); //进入小括号运算,str_brace用于返回替换答案的指针字符串
        free(brace1);                            //释放brace1内存
        free(brace);                             //释放brace内存
        //完成替换

        //初始化所有指针，用于二次运算
        left_brace = NULL;
        right_brace = NULL;
        brace = NULL;
        brace1 = NULL;

        //程序调试用
        //printf("大括号内的字符串是%s", brace);
    }
    /*results_string = */ Parenthesis(string); //进入小括号运算
    //Braces(results_string);
    DEBUG("exit function.");
    return string;
}

/**
 * @brief 取小括号运算
 * 
 * @param string 字符串
 * @return char* 运算后的字符串指针
 */
char *Parenthesis(char *string)
{
    //left_parenthesis指针指向左边的小括号,parenthesis指针用于存放小括号内的数,parenthesis1用于缓存"("到")"的字符并用于删除
    char *str = string, *left_parenthesis = NULL, *right_parenthesis = NULL,
         *parenthesis = NULL, *parenthesis1 = NULL, *results_string = NULL /*, *str_parenthesis = NULL*/;
    DEBUG("entry function.");

    while ((strchr(str, ')') != NULL) && (strchr(str, '(') != NULL)) //检测小括号，没有就不成立
    {
        right_parenthesis = strchr(str, ')');
        for (unsigned int i = 0; i < strlen(string); i++, str++)
        {
            if (*str == '\0')
                break;
            else if (*str == ')')
                break;
            else if (*str == '(')
                left_parenthesis = str;
        }

        //开始申请空间存放"("到")"的字符
        parenthesis = calloc(1024, 1);
        memset(parenthesis, 0, 1023);                                                 //初始化parenthesis内存
        left_parenthesis++;                                                           //指针右移开始复制()中的字符串
        strncpy(parenthesis, left_parenthesis, right_parenthesis - left_parenthesis); //开始复制()中到parenthesis
        left_parenthesis--;                                                           //将(指针还原为原来的指向的(

        //str_parenthesis = multiplication_division_remainder(parenthesis);		//进入小括号运算,str_parenthesis用于返回替换答案的指针字符串

        //准备替换"("到")"的字符
        parenthesis1 = calloc(1024, 1);
        memset(parenthesis1, 0, 1023);                                                 //初始化parenthesis1内存
        right_parenthesis++;                                                           //指针右移开始复制()中的字符串
        strncpy(parenthesis1, left_parenthesis, right_parenthesis - left_parenthesis); //给被删除的字符串加上()
        right_parenthesis--;                                                           //将)指针还原为原来的指向的)
        //开始替换字符
        str = string; //复原str指针

        //还原指针
        strrpc(str, parenthesis1, multiplication_division_remainder(parenthesis)); //进入小括号运算,str_parenthesis用于返回替换答案的指针字符串
        free(parenthesis1);                                                        //释放parenthesis1内存
        free(parenthesis);                                                         //释放parenthesis内存
        //完成替换

        //所有指针初始化,用于二次使用
        left_parenthesis = NULL;
        right_parenthesis = NULL;
        parenthesis = NULL;
        parenthesis1 = NULL;
        results_string = NULL;

        //程序调试用
        //printf("小括号内的字符串是%s", parenthesis);
    }
    /*results_string = */ multiplication_division_remainder(string); //进入乘法and除余法运算
    DEBUG("exit function.");
    return string;
}

/**
 * @brief 运算+-/*%
 * 
 * @param string 字符串
 * @param ch 运算格式(+-/*%)
 * @return char* 被替换结果后的字符串
 */
char *operation(char *string, char ch)
{
    int i;
    char *p_str = string;
    char *p_char[3] = {NULL};                                           /* 定义3个临时指针，0指向左边的数，1指向右边的数，2指向运算符 */
    char num_char_buf_left[1024] = {0}, num_char_buf_right[1024] = {0}; /* 存放左右边的数字 */
    double dou_left, dou_right, dou_results;                            /* 存放计算结果 */
    DEBUG("entry function.");

    p_char[2] = strchr(string, ch);
    p_char[0] = string;
    p_char[1] = p_char[2] + 1;

    /* 截取左边字符串 */
    for (i = 0; p_char[0][i] != ch; ++i)
    {
        num_char_buf_left[i] = p_char[0][i];
    }
    /* 转成实数 */
    if (strchr(num_char_buf_left, '.') != NULL)
    {
        dou_left = atof(num_char_buf_left);
    }
    else
    {
        dou_left = (double)atoi(num_char_buf_left);
    }

    /* 截取右边的字符串 */
    for (i = 0; p_char[1][i] != '\0'; ++i)
    {
        num_char_buf_right[i] = p_char[1][i];
    }
    /* 转成实数 */
    if (strchr(num_char_buf_right, '.') != NULL)
    {
        dou_right = atof(num_char_buf_right);
    }
    else
    {
        dou_right = (double)atoi(num_char_buf_right);
    }

    /* 计算dou_results的结果 */
    switch (ch)
    {
    case '+':
        dou_results = dou_left + dou_right;
        break;
    case '-':
        dou_results = dou_left - dou_right;
        break;
    case '*':
        dou_results = dou_left * dou_right;
        break;
    case '/':
        dou_results = dou_left / dou_right;
        break;
    case '^':
        dou_results = pow(dou_left, dou_right); /* 只有这里依赖math库 */
        break;
    default:
        FATAL("can't recognize char of '%c'.", ch);
    }
    sprintf(string, "%lf", dou_results);

    DEBUG("exit function.");
    return string;
}

/**
 * @brief 加减运算
 * 
 * @param string 字符串
 * @return char* 结果字符串指针
 */
char *plus_less(char *string)
{
    char *str = string, m[] = "+-";
    DEBUG("entry function.");

    while ((strchr(str, '+') != NULL) || (strchr(str, '-') != NULL))
    {
        if (str[0] == '-') //防止负数而进入死循环
            break;
        for (int i = 0; i < strlen(str); i++)
        {
            for (int j = 0; j < strlen(m); j++)
            {
                if (str[i] == m[j])
                {
                    switch (str[i])
                    {
                    case '+':
                        operation(str, '+');
                        break;
                    case '-':
                        operation(str, '-');
                        break;
                    default:
                        break;
                    }
                }
            }
        }
    }

    if (check_numbers(string) == 1)
    {
        DEBUG("exit function.");
        return string; //返回结果
    }
    DEBUG("exit function.");
}

/**
 * @brief 乘除余运算，按优先级计算
 * 
 * @param string 字符串
 * @return char* 结果字符串指针
 */
char *multiplication_division_remainder(char *string)
{
    char *str = string, m[] = "*/%^";
    DEBUG("entry function.");

    while ((strchr(str, '*') != NULL) || (strchr(str, '/') != NULL) ||
           (strchr(str, '%') != NULL) || (strchr(str, '^') != NULL))
    {
        if (strchr(str, '^') != NULL)
        {
            operation(str, '^');
        }
        else
        {
            for (int i = 0; i < strlen(str); i++)
            {
                for (int j = 0; j < strlen(m); j++)
                {
                    if (str[i] == m[j])
                    {
                        switch (str[i])
                        {
                        case '*':
                            operation(str, '*');
                            break;
                        case '/':
                            operation(str, '/');
                            break;
                        case '%':
                            operation(str, '%');
                            break;
                        default:
                            break;
                        }
                    }
                }
            }
        }
    }
    plus_less(string);
    DEBUG("exit function.");
    return string;
}

/* ******************************************************************************************* */
/* **************************************非法字符检查模块**************************************** */
/**
 * @brief check the input string error.
 * 
 * @param s inputer, allow "()+-*\^1234567890."
 * @return int pass zero, No pass -1
 */
int calc_check_error_char(char *str)
{
    char const *s_index;

    for (s_index = str; *s_index != '\0'; ++s_index)
    {
        /* [40-43] "()*+" */
        if ('(' <= *s_index && *s_index <= '+')
            continue;
        /* [45-57] "-./0123456789" */
        if ('-' <= *s_index && *s_index <= '9')
            continue;
        /* 94 ^ */
        if ('^' == *s_index)
            continue;

        return -1;
    }
    return 0;
}

/**
 * @brief check brackets
 * 
 * @param s input string
 * @return int result code
 */
int calc_check_error_brackets(char *s)
{
    int16_t brackets_count = 0;
    int16_t s_len;
    int16_t i;
    char *s_ptr;

    s_len = strlen(s);
    s_ptr = s;

    for (i = 0; i < s_len; ++i, s_ptr++)
    {
        if (*s_ptr == '(' || *s_ptr == ')')
        {
            brackets_count++;
        }
    }

    if ((brackets_count % 2) != 0)
    {
        return -1;
    }

    return 0;
}
/* **************************************非法字符检查模块**************************************** */
/* ******************************************************************************************* */

/* ******************************************************************************************* */
/* **************************************字符串操作模块**************************************** */
/**
 * @brief 格式化字符串，去除字符杂质
 * 
 * @param string inputer string.
 */
void format_the_entered_string(char *string)
{
    char *s_ptr =  string, *s_kongge_ptr;
    int32_t s_len;
    int i;

    s_len = strlen(string);
    for(; *s_ptr != '\0'; ++s_ptr)
    {
        if(*s_ptr == ' ')
        {
            for(s_kongge_ptr = s_ptr; *s_kongge_ptr != '\0'; ++s_kongge_ptr)
            {
                *s_kongge_ptr = *(s_kongge_ptr + 1);
            }
            *s_kongge_ptr = '\0';
        }
    }
}

/**
 * @brief 判断字符是否为数字或小数点
 * 
 * @param ch 字符
 * @return int 是1，否0
 */
int judgment_number(char ch)
{
    char string[] = "0123456789.";
    //strchr() in string.h
    if (strchr(string, ch) != NULL)
        return 1;
    else
        return 0;
}

/**
 * @brief 检测字符串是否为纯数字或小数点
 * 
 * @param string 字符串
 * @return int 是1，否2
 */
int check_numbers(char *const string)
{
    char *str = string;
    char num[] = "0123456789.";
    unsigned int temp = 0;
    for (int i = 0; i < strlen(str) - 1; i++)
    {
        for (int j = 0; j < strlen(num); j++)
        {
            if (str[i] == num[j])
                temp++;
        }
    }
    if (temp == strlen(string) - 1)
        return 1;
    else
        return 0;
}

/* 功  能：将str字符串中的oldstr字符串替换为newstr字符串
* 参  数：str：操作目标 oldstr：被替换者 newstr：替换者
* 返回值：返回替换之后的字符串
* 版  本： V0.2
*/
char *strrpc(char *str, char *oldstr, char *newstr)
{
    unsigned int str_lenth = strlen(str);
    char *bstr = calloc(str_lenth, 1); //转换缓冲区
    memset(bstr, 0, str_lenth);

    for (unsigned int i = 0; i < strlen(str); i++)
    {
        if (!strncmp(str + i, oldstr, strlen(oldstr)))
        { //查找目标字符串
            strcat(bstr, newstr);
            i += strlen(oldstr) - 1;
        }
        else
        {
            strncat(bstr, str + i, 1); //保存一字节进缓冲区
        }
    }

    strcpy(str, bstr);
    return str;
}
/* **************************************字符串操作模块**************************************** */
/* ****************************************************************************************** */
